import Singleton from "../net/Singleton";
import CsvUtils from "../utils/CsvUtils";
import LogUtil from "../utils/LogUtil";

export default class ResManager extends Singleton {
    _ResDataJson = {};
    _cache = {};

    /**
     * 预加载资源
     * @param obj 
     * @param callback 
     * @param node 
     * @returns 
     */
    load(obj: ResOptions, callback: Function = null, node: cc.Node = null) {
        let _restype = null;
        switch (obj.resourceType) {
            case 'csv':
                _restype = cc.TextAsset;
                break;
            case 'image':
                _restype = cc.SpriteFrame;
                break;
            case 'spine':
                _restype = sp.SkeletonData;
                break;
            case 'prefab':
                _restype = cc.Prefab;
                break;
            case 'mp3':
                _restype = cc.AudioClip;
                break;
            default: break
        }

        let _asset = this.getRes(obj.resourceId);
        if (_asset) {
            // LogUtil.log(LogUtil.getFileName(),'资源已加载');
            this.loadCompete(_asset, node, callback);
            return;
        }

        cc.resources.load(obj.resourcePath, _restype, (error: Error, asset: any) => {
            if (error) {
                LogUtil.error(error);
            } else {
                switch (obj.resourceType) {
                    case 'csv':
                        let txt = CsvUtils.ins().csv2json(asset.text, obj.key, obj.resourceId);
                        this._ResDataJson[obj.resourceId] = txt;
                        this.loadCompete(txt, node, callback)
                        break;
                    case 'image':
                        this._cache[obj.resourceId] = asset;
                        this.loadCompete(asset, node, callback)
                        break;
                    case 'spine':
                        this._cache[obj.resourceId] = asset;
                        this.loadCompete(asset, node, callback);
                        break;
                    case 'prefab':
                        this._cache[obj.resourceId] = asset;
                        this.loadCompete(asset, node, callback);
                        break;
                    case 'mp3':
                        this._cache[obj.resourceId] = asset;
                        this.loadCompete(asset, node, callback);
                        break;
                    default: break
                }
            }
        });
    }

    /**
     * 加载成功回调
     * @param asset 
     * @param node 
     * @param callback 
     */
    loadCompete(asset: any, node: cc.Node, callback: Function) {
        if (node && node.isValid && callback && asset) {
            callback(asset);
        }
    }

    /**
     * 加载单个资源
     * @param name 
     * @param resourceType 
     * @param callback 
     * @param node 
     */
    loadSingle(name: string, resourceType: string, callback: Function, node: cc.Node) {
        let resOptions = {
            resourceId: name,
            key: [],
            resourceType: resourceType,
            resourcePath: name
        };
        this.load(resOptions, callback, node)
    }

    /**
     * 加载.manifest文件
     * @param name 
     * @param callback 
     */
    loadManifest(name: string, callback: Function) {
        cc.resources.load(name, cc.Asset, (error, asset: cc.Asset) => {
            if (error) {
                LogUtil.error(error);
            } else {
                LogUtil.log('本地路径nativeUrl>>', asset.nativeUrl);
                callback(asset);
            }
        });
    }


    /**
     * 获取缓存资源
     * @param key 
     * @returns 
     */
    getRes(key: string) {
        if (this._cache[key]) {
            return this._cache[key];
        }
        if (this._ResDataJson[key]) {
            return this._ResDataJson[key]
        }
        // LogUtil.warn("资源未加载:", key);
        return null;
    }

    /**
     * 异步加载资源
     * @param obj 
     * @returns 
     */
    loadAsync(obj: ResOptions) {
        let _restype = null;
        switch (obj.resourceType) {
            case 'csv':
                _restype = cc.TextAsset;
                break;
        }
        return new Promise((resolve, reject) => {
            cc.resources.load(obj.resourcePath, _restype, (error: Error, asset: any) => {
                if (error) {
                    LogUtil.error(error);
                    reject('error');
                } else {
                    switch (obj.resourceType) {
                        case 'csv':
                            let txt = CsvUtils.ins().csv2json(asset.text, obj.key, obj.resourceId);
                            this._ResDataJson[obj.resourceId] = txt;
                            resolve(txt)
                            break;
                    }
                }
            })
        })
    }

    /**
     * 异步获取资源
     * @param obj 
     * @param node 
     * @param callback 
     * @returns 
     */
    async getResAsync(obj: ResOptions, node: cc.Node = null, callback: Function = null) {
        if (!this._ResDataJson[obj.resourceId]) {
            let asset = await this.loadAsync(obj);
            return asset;
        } else {
            return this._ResDataJson[obj.resourceId];
        }
    }

    /**
     * 加载远程资源或者本地资源
     * 远程 url 带图片后缀名
     * var remoteUrl = "http://unknown.org/someres.png";
     * 用绝对路径加载设备存储内的资源，比如相册
     * var absolutePath = "/dara/data/some/path/to/image.png"
     * @param remoteUrl 
     * @param node 
     * @param callback 
     */
    loadRemote(remoteUrl: string, node: cc.Node, callback: Function = null) {
        cc.assetManager.loadRemote(remoteUrl, function (err, asset) {
            if (err) {
                LogUtil.error(err);
                return
            }
            if (node && node.isValid && callback && asset) {
                callback(asset)
            }
        });

        // 远程 url 不带图片后缀名，此时必须指定远程图片文件的类型
        // remoteUrl = "http://unknown.org/emoji?id=124982374";
        // cc.assetManager.loadRemote(remoteUrl, {ext: '.png'}, function () {
        //     // Use texture to create sprite frame
        // });
    }

    /**
     * 资源释放
     * @param asset 
     */
    releaseAsset(asset: cc.Asset) {
        cc.assetManager.releaseAsset(asset);
        // cc.resources.release("test assets/image", cc.SpriteFrame);
    }


    /** 计算当前纹理数量和缓存 */
    public computeTextureCache() {
        let cache = cc.assetManager.assets;
        let totalTextureSize = 0;
        let count = 0;
        cache.forEach((item: cc.Asset, key: string) => {            
            let type = (item && item['__classname__']) ? item['__classname__'] : '';
            if(type == 'cc.Texture2D') {
                let texture = item as cc.Texture2D;
                let textureSize = texture.width * texture.height * ((texture['_native'] === '.jpg' ? 3 : 4) / 1024 / 1024);
                // debugger
                totalTextureSize += textureSize;
                count ++;
            }
        });
        return `缓存 [纹理总数:${count}][纹理缓存:${totalTextureSize.toFixed(2) + 'M'}]`;
    }

}

/**
 * @param resourceId 资源ID
 * @param key 配置表主键
 * @param resourceType 资源类型
 * @param resourcePath 资源路径
 */
export interface ResOptions {
    resourceId: string;
    key: string[];
    resourceType: string;
    resourcePath: string;
}
